package com.uinnova.product.eam.service.es;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.binary.core.exception.BinaryException;
import com.binary.core.lang.StringUtils;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.vmdb.comm.bean.CIState;
import com.uinnova.product.vmdb.comm.model.ci.*;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CiGroupPage;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.product.vmdb.provider.search.bean.CcCiClassObj;
import com.uinnova.product.vmdb.provider.search.bean.CcCiObj;
import com.uinnova.product.vmdb.provider.search.bean.CcCiSearchData;
import com.uinnova.product.vmdb.provider.search.bean.CcCiSearchPage;
import com.uino.bean.cmdb.base.ESCIClassInfo;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.ESCITagInfo;
import com.uino.bean.cmdb.query.*;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.cmdb.ESRltClassSvc;
import com.uino.dao.cmdb.ESTagSvc;
import com.uino.dao.util.ESUtil;
import com.uino.service.permission.microservice.IUserSvc;
import com.uino.util.sys.CheckAttrUtil;
import com.uino.util.sys.SysUtil;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.MultiMatchQueryBuilder.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 共用服务
 *
 * @author guojun
 */
@Service
public class IamsESCmdbCommPrivateSvc {

    private static final Logger log = LoggerFactory.getLogger(IamsESCmdbCommPrivateSvc.class);

    @Autowired
    private IUserSvc userSvc;

    @Autowired
    @Lazy
    private ESCIClassSvc classSvc;

    @Autowired
    @Lazy
    private ESRltClassSvc esRltClassSvc;

    @Autowired
    private ESTagSvc tagSvc;

    @Autowired
    private IamsESCIPrivateSvc ciSvc;

    /**
     * RSM-SLAVE访问入口
     **/
    @Value("${http.resource.space}")
    private String rsmSlaveRoot;

    @Value("${local.resource.space}")
    private String localPath;

    /**
     * ES CI对象-ESCIInfo转化成CcCiInfo
     *
     * @param esCI
     * @param hasClass 是否包含分类信息
     * @return
     */
    public CcCiInfo tranCcCiInfo(ESCIInfo esCI, boolean hasClass) {
        if (esCI == null) {
            return null;
        }
        String jsonStr = JSON.toJSONString(esCI, SerializerFeature.WriteMapNullValue).replaceAll("null", "''");
        JSONObject json = JSON.parseObject(jsonStr);
        CcCiInfo ciInfo = JSON.toJavaObject(json, CcCiInfo.class);
        CcCi ci = JSON.toJavaObject(json, CcCi.class);
        // CcCiInfo添加版本标识
        Long version = esCI.getVersion() == null ? 0 : esCI.getVersion();
        ci.setCiVersion(String.valueOf(version));
        ciInfo.setCi(ci);
        if (hasClass) {
            Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
            List<ESCIClassInfo> classInfos = classSvc.getAllDefESClassInfosByClassIds(domainId, Arrays.asList(ci.getClassId()));
            if (!BinaryUtils.isEmpty(classInfos)) {
                ESCIClassInfo ciClass = classInfos.get(0);
                ciInfo.setCiClass(ciClass);
                ciInfo.setAttrDefs(ciClass.getCcAttrDefs());
            }
        }
        return ciInfo;
    }

    /**
     * ES CI对象-CcCiInfo转化成ESCIInfo
     *
     * @param ciInfo 该CI所属分类的全部属性
     * @return
     */
    public ESCIInfo tranESCIInfo(CcCiInfo ciInfo) {
        String jsonStr = JSON.toJSONString(ciInfo.getCi());
        JSONObject json = JSON.parseObject(jsonStr);
        ESCIInfo esCI = JSON.toJavaObject(json, ESCIInfo.class);
        String ciVersion = ciInfo.getCi().getCiVersion();
        if (BinaryUtils.isEmpty(ciVersion) || ciVersion.equals("0")) {
            ciVersion = "1";
        }
        esCI.setVersion(Long.parseLong(ciVersion));
        if (!BinaryUtils.isEmpty(ciInfo.getAttrs())) {
            String ms = JSON.toJSONString(ciInfo.getAttrs());
            esCI.setAttrs(JSON.parseObject(ms));
        }
        return esCI.ciAttrformat();
    }

    public void tranESCIInfo(ESCIInfo esCI) {
        String ciVersion = esCI.getCiVersion();
        if (BinaryUtils.isEmpty(ciVersion) || ciVersion.equals("0")) {
            ciVersion = "1";
        }
        esCI.setVersion(Long.parseLong(ciVersion));
    }

    /**
     * ES分类对象-CIClassInfo转化成CcCiClassInfo
     *
     * @param ciClass
     * @return
     */
    public CcCiClassInfo tranCcCiClassInfo(ESCIClassInfo ciClass) {
        if (BinaryUtils.isEmpty(ciClass)) {
            return null;
        }
        String jsonStr = JSON.toJSONString(ciClass);
        JSONObject json = JSON.parseObject(jsonStr);
        CcCiClassInfo ciClassInfo = JSON.toJavaObject(json, CcCiClassInfo.class);
        CcCiClass ciclass = JSON.toJavaObject(json, CcCiClass.class);
        String icon = BinaryUtils.isEmpty(ciclass.getIcon()) ? classSvc.getDefaultIcon() : ciclass.getIcon();
        if (icon.startsWith(rsmSlaveRoot)) {
            icon = icon.replaceAll(rsmSlaveRoot, "");
        }
        ciclass.setIcon(rsmSlaveRoot + icon);
        ciClassInfo.setCiClass(ciclass);
        List<CcCiAttrDef> attrs = ciClass.getCcAttrDefs();
        CcFixAttrMapping fixMapping = new CcFixAttrMapping();
        if (attrs != null) {
            for (CcCiAttrDef attr : attrs) {
                if (attr.getIsMajor() != null && attr.getIsMajor() == 1) {
                    fixMapping.setNmCiCode(attr.getProName());
                    fixMapping.setId(attr.getId());
                }
            }
        }
        ciClassInfo.setFixMapping(fixMapping);
        return ciClassInfo;
    }

    /**
     * CcCiClassInfo转化成ES分类对象-CIClassInfo
     *
     * @param clsInfo
     * @return
     */
    public ESCIClassInfo tranESCIClassInfo(CcCiClassInfo clsInfo) {
        String jsonStr = JSON.toJSONString(clsInfo.getCiClass());
        JSONObject json = JSON.parseObject(jsonStr);
        ESCIClassInfo esCI = JSON.toJavaObject(json, ESCIClassInfo.class);
        if (clsInfo.getAttrDefs() != null) {
            esCI.setCcAttrDefs(clsInfo.getAttrDefs());
        }
        return esCI;
    }

    public Page<CcCiInfo> transEsInfoPage(Page<ESCIInfo> esPage, Boolean hasClass) {
        Page<CcCiInfo> page = new Page<>();
        page.setPageNum(esPage.getPageNum());
        page.setPageSize(esPage.getPageSize());
        page.setTotalPages(esPage.getTotalPages());
        page.setTotalRows(esPage.getTotalRows());
        List<ESCIInfo> esInfos = esPage.getData();
        List<CcCiInfo> ciInfos = transEsInfoList(esInfos, hasClass);
        page.setData(ciInfos);
        return page;
    }

    public List<CcCiInfo> transEsInfoList(List<ESCIInfo> esInfos, Boolean hasClass) {
        List<CcCiInfo> ciInfos = new ArrayList<>();
        if (BinaryUtils.isEmpty(esInfos)) {
            return ciInfos;
        }
        Map<Long, ESCIClassInfo> clsMap = new HashMap<>();
        if (hasClass) {
            Set<Long> ids = new HashSet<>();
            for (ESCIInfo esInfo : esInfos) {
                ids.add(esInfo.getClassId());
            }
            if (!BinaryUtils.isEmpty(ids)) {
                // 分类组装
                CCcCiClass cdt = new CCcCiClass();
                cdt.setIds(ids.toArray(new Long[ids.size()]));
//                Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
                List<ESCIClassInfo> clsList = classSvc.getAllDefESClassInfosByClassIds(1L, ids);
                // List<CcCiClassInfo> classes = classSvc.queryClassByCdt(1,
                // 2000, cdt);
                for (ESCIClassInfo classInfo : clsList) {
                    clsMap.put(classInfo.getId(), classInfo);
                }
            }
        }
        for (ESCIInfo esciInfo : esInfos) {
            String jsonStr = JSON.toJSONString(esciInfo, SerializerFeature.WriteMapNullValue).replaceAll("null", "''");
            JSONObject json = JSON.parseObject(jsonStr);
            CcCiInfo ciInfo = JSON.toJavaObject(json, CcCiInfo.class);
            CcCi ci = JSON.toJavaObject(json, CcCi.class);
            Long version = esciInfo.getVersion() == null ? 1 : esciInfo.getVersion();
            ci.setCiVersion(String.valueOf(version));
            ciInfo.setCi(ci);
            if (hasClass) {
                ciInfo.setCiClass(clsMap.get(esciInfo.getClassId()));
                ciInfo.setAttrDefs(clsMap.get(esciInfo.getClassId()).getCcAttrDefs());
            }
            ciInfos.add(ciInfo);
        }
        return ciInfos;
    }

    /**
     * <b>对象转换成CiGroupPage
     *
     * @param page
     * @return
     */
    public CiGroupPage tranCiGroupPage(Page<ESCIInfo> page, boolean hasClass) {
        CiGroupPage rs = new CiGroupPage();
        rs.setPageNum(page.getPageNum());
        rs.setPageSize(page.getPageSize());
        rs.setTotalRows(page.getTotalRows());
        rs.setTotalCiCount(page.getTotalRows());
        rs.setTotalPages(page.getTotalPages());
        List<ESCIInfo> esCIs = page.getData() == null ? new ArrayList<>() : page.getData();
        Set<Long> classIds = esCIs.stream().filter(ci -> ci.getClassId() != null).map(ESCIInfo::getClassId)
                .collect(Collectors.toSet());
        Map<Long, CcCiClassInfo> classMap = new HashMap<Long, CcCiClassInfo>();
        if (hasClass) {
            if (classIds.size() > 0) {
                Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
                List<ESCIClassInfo> classInfos = classSvc.getAllDefESClassInfosByClassIds(domainId, classIds);
                if (classInfos != null) {
                    for (ESCIClassInfo esClsInfo : classInfos) {
                        CcCiClassInfo classInfo = tranCcCiClassInfo(esClsInfo);
                        if (rs.getClassInfos() == null) {
                            rs.setClassInfos(new ArrayList<>());
                        }
                        rs.getClassInfos().add(classInfo);
                        classMap.put(classInfo.getCiClass().getId(), classInfo);
                    }
                }
            }
        }
        List<CcCiInfo> cis = new ArrayList<CcCiInfo>();
        for (ESCIInfo esCI : esCIs) {
            if (hasClass) {
                CcCiClassInfo classInfo = classMap.get(esCI.getClassId());
                if (classInfo.getCiCount() == null) {
                    classInfo.setCiCount(0L);
                }
                classInfo.setCiCount(classInfo.getCiCount() + 1);
                CcCiInfo ci = tranCcCiInfo(esCI, false);
                ci.setCiClass(classInfo.getCiClass());
                ci.setAttrDefs(classInfo.getAttrDefs());
                JSONArray labels = new JSONArray();
                for (CcCiAttrDef def : classInfo.getAttrDefs()) {
                    if (def.getIsCiDisp() > 0) {
                        labels.add(ci.getAttrs().get(def.getProStdName()));
                    }
                }
                ci.getCi().setCiLabel(labels.toString());
                cis.add(ci);
            } else {
                cis.add(tranCcCiInfo(esCI, false));
            }
        }
        rs.setData(cis);
        return rs;
    }

    /**
     * <b> 对象转换CcCiSearchPage
     *
     * @param page
     * @return
     */
    public CcCiSearchPage tranCcCiSearchPage(Page<ESCIInfo> page) {
        CcCiSearchPage rs = new CcCiSearchPage();
        long totalCount = page.getTotalRows();
        int hasNext = 1;
        if (page.getPageNum() * page.getPageSize() >= totalCount) {
            hasNext = 0;
        }
        // 结果对象组装
        CcCiSearchData data = new CcCiSearchData();
        data.setTotalCount(page.getTotalRows());
        // 记录组装
        List<CcCiObj> records = new ArrayList<CcCiObj>();
        List<ESCIInfo> esCIs = page.getData();
        Set<Long> ids = new HashSet<Long>();
        for (ESCIInfo esCI : esCIs) {
            CcCiInfo ciInfo = tranCcCiInfo(esCI, false);
            CcCiObj record = new CcCiObj();
            record.setCi(ciInfo.getCi());
            record.setAttrs(ciInfo.getAttrs());
            records.add(record);
            Long classId = esCI.getClassId();
            ids.add(classId);
        }
        // 分类组装
        CCcCiClass cdt = new CCcCiClass();
        Long[] classIds = new Long[ids.size()];
        classIds = ids.toArray(classIds);
        cdt.setIds(classIds);
        List<CcCiClassInfo> classes = classSvc.queryClassByCdt(1, 2000, cdt);
        Map<Long, CcCiClassObj> classMp = new HashMap<Long, CcCiClassObj>();
        for (CcCiClassInfo clsInfo : classes) {
            CcCiClassObj obj = new CcCiClassObj();
            CcCiClass cls = clsInfo.getCiClass();
            obj.setAttrDefs(clsInfo.getAttrDefs());
            obj.setCls(cls);
            obj.setFix(clsInfo.getFixMapping());
            classMp.put(cls.getId(), obj);
        }
        data.setClassMp(classMp);
        data.setRecords(records);
        data.setHasNext(hasNext);
        rs.setData(data);
        rs.setPageNum(page.getPageNum());
        rs.setPageSize(page.getPageSize());
        rs.setTotalRows(totalCount);
        rs.setTotalPages(page.getTotalPages());
        return rs;
    }

    /**
     * <b> CI关系分页对象转换
     *
     * @param page
     * @return
     */
    public Page<CcCiRltInfo> tranCcCiRltInfoPage(Page<ESCIRltInfo> page) {
        List<ESCIRltInfo> rlts = page.getData();
        Set<Long> ciIdSet = new HashSet<Long>();
        Set<Long> classIdSet = new HashSet<Long>();
        for (ESCIRltInfo rlt : rlts) {
            ciIdSet.add(rlt.getSourceCiId());
            ciIdSet.add(rlt.getTargetCiId());
            classIdSet.add(rlt.getSourceClassId());
            classIdSet.add(rlt.getTargetClassId());
        }
        // 分类组装
        CCcCiClass cdt = new CCcCiClass();
        Long[] classIds = new Long[classIdSet.size()];
        cdt.setIds(classIdSet.toArray(classIds));
        List<CcCiClassInfo> classes = classSvc.queryClassByCdt(1, 2000, cdt);
        Map<Long, CcCiClassInfo> classMp = new HashMap<Long, CcCiClassInfo>();
        for (CcCiClassInfo cls : classes) {
            classMp.put(cls.getCiClass().getId(), cls);
        }
        // CI组装
        List<ESCIInfo> esCIs = ciSvc.getListByQuery(1, 2000, QueryBuilders.termsQuery("id", ciIdSet)).getData();
        Map<Long, CcCiInfo> ciInfoMap = new HashMap<Long, CcCiInfo>();
        for (ESCIInfo esCI : esCIs) {
            CcCiInfo ciInfo = tranCcCiInfo(esCI, false);
            CcCiClassInfo ciClass = classMp.get(esCI.getClassId());
            if (ciClass != null) {
                CcCiClass cls = ciClass.getCiClass();
                ciInfo.setCiClass(cls);
                List<CcCiAttrDef> attrDefs = ciClass.getAttrDefs();
                ciInfo.setAttrDefs(attrDefs);
                CcFixAttrMapping fix = ciClass.getFixMapping();
                ciInfo.setFixMapping(fix);
            }
            ciInfoMap.put(esCI.getId(), ciInfo);
        }
        // 结果转化
        List<CcCiRltInfo> data = new ArrayList<CcCiRltInfo>();
        Set<String> set = new HashSet<>();
        for (ESCIRltInfo r : rlts) {
            CcCiRltInfo rltInfo = tranCcCiRltInfo(r, false);
            Long sourceId = r.getSourceCiId();
            Long targetId = r.getTargetCiId();
            CcCiInfo sourceCiInfo = ciInfoMap.get(sourceId);
            CcCiInfo targetCiInfo = ciInfoMap.get(targetId);
            if (sourceCiInfo != null) {
                rltInfo.setSourceCiInfo(sourceCiInfo);
            }
            if (targetCiInfo != null) {
                rltInfo.setTargetCiInfo(targetCiInfo);
            }
            String ownerCode = rltInfo.getCiRlt().getOwnerCode();
            if (!StringUtils.isEmpty(ownerCode)) {
                set.add(ownerCode);
            }
            data.add(rltInfo);
        }
        String[] codes = set.toArray(new String[set.size()]);
        Map<String, SysUser> userMap = new HashMap<>();
        if (codes.length > 0) {
            CSysUser user = new CSysUser();
            user.setLoginCodes(codes);
            List<SysUser> sysUserByCdt = userSvc.getSysUserByCdt(user);
            userMap = sysUserByCdt.stream().collect(Collectors.toMap(SysUser::getLoginCode, each -> each));
        }
        for(CcCiRltInfo info : data) {

            Map<String, String> attrs = info.getAttrs();
            if (info.getAttrs() == null) {
                attrs= new HashMap<>();
                info.setAttrs(attrs);
            }
            String userName = "admin";
            SysUser currentUser = userMap.get(info.getCiRlt().getOwnerCode());
            if (!BinaryUtils.isEmpty(currentUser)) {
                userName = currentUser.getUserName() + "(" + currentUser.getLoginCode() + ")";
            }
            attrs.put("所属用户", userName);
        }


        Page<CcCiRltInfo> rs = new Page<CcCiRltInfo>();
        rs.setData(data);
        rs.setPageNum(page.getPageNum());
        rs.setPageSize(page.getPageSize());
        rs.setTotalPages(page.getTotalPages());
        rs.setTotalRows(page.getTotalRows());
        return rs;
    }

    public List<CcCiRltInfo> tranCcCiRltInfoList(List<ESCIRltInfo> rlts ) {
        Set<Long> ciIdSet = new HashSet<Long>();
        Set<Long> classIdSet = new HashSet<Long>();
        for (ESCIRltInfo rlt : rlts) {
            ciIdSet.add(rlt.getSourceCiId());
            ciIdSet.add(rlt.getTargetCiId());
            classIdSet.add(rlt.getSourceClassId());
            classIdSet.add(rlt.getTargetClassId());
        }
        // 分类组装
        CCcCiClass cdt = new CCcCiClass();
        Long[] classIds = new Long[classIdSet.size()];
        cdt.setIds(classIdSet.toArray(classIds));
        List<CcCiClassInfo> classes = classSvc.queryClassByCdt(1, 2000, cdt);
        Map<Long, CcCiClassInfo> classMp = new HashMap<Long, CcCiClassInfo>();
        for (CcCiClassInfo cls : classes) {
            classMp.put(cls.getCiClass().getId(), cls);
        }
        // CI组装
        List<ESCIInfo> esCIs = ciSvc.getListByQuery(1, 2000, QueryBuilders.termsQuery("id", ciIdSet)).getData();
        Map<Long, CcCiInfo> ciInfoMap = new HashMap<Long, CcCiInfo>();
        for (ESCIInfo esCI : esCIs) {
            CcCiInfo ciInfo = tranCcCiInfo(esCI, false);
            CcCiClassInfo ciClass = classMp.get(esCI.getClassId());
            if (ciClass != null) {
                CcCiClass cls = ciClass.getCiClass();
                ciInfo.setCiClass(cls);
                List<CcCiAttrDef> attrDefs = ciClass.getAttrDefs();
                ciInfo.setAttrDefs(attrDefs);
                CcFixAttrMapping fix = ciClass.getFixMapping();
                ciInfo.setFixMapping(fix);
            }
            ciInfoMap.put(esCI.getId(), ciInfo);
        }
        // 结果转化
        List<CcCiRltInfo> data = new ArrayList<CcCiRltInfo>();
        for (ESCIRltInfo r : rlts) {
            CcCiRltInfo rltInfo = tranCcCiRltInfo(r, false);
            Long sourceId = r.getSourceCiId();
            Long targetId = r.getTargetCiId();
            CcCiInfo sourceCiInfo = ciInfoMap.get(sourceId);
            CcCiInfo targetCiInfo = ciInfoMap.get(targetId);
            if (sourceCiInfo != null) {
                rltInfo.setSourceCiInfo(sourceCiInfo);
            }
            if (targetCiInfo != null) {
                rltInfo.setTargetCiInfo(targetCiInfo);
            }
            data.add(rltInfo);
        }
        return data;
    }

    /**
     * <b> CI关系转换
     *
     * @param esCIRltInfo ES关系对象
     * @param hasCI       是否包含CI
     * @return
     */
    public CcCiRltInfo tranCcCiRltInfo(ESCIRltInfo esCIRltInfo, boolean hasCI) {
        CcCiRltInfo ciRltInfo = new CcCiRltInfo();
        ciRltInfo.setCiRlt(esCIRltInfo);
        if (esCIRltInfo.getAttrs() != null && !esCIRltInfo.getAttrs().isEmpty()) {
            ciRltInfo.setAttrs(esCIRltInfo.getAttrs());
        }
        if (hasCI) {
            long sourceId = esCIRltInfo.getSourceCiId();
            long targetId = esCIRltInfo.getTargetCiId();
            CcCiInfo sourceCiInfo = ciSvc.getCiInfoById(sourceId);
            CcCiInfo targetCiInfo = ciSvc.getCiInfoById(targetId);
            if (sourceCiInfo != null) {
                ciRltInfo.setSourceCiInfo(sourceCiInfo);
            }
            if (targetCiInfo != null) {
                ciRltInfo.setTargetCiInfo(targetCiInfo);
            }
        }
        return ciRltInfo;
    }

    /**
     * <b>根据查询Bean 获取CI
     *
     * @param bean
     * @return
     */
    public QueryBuilder getCIQueryBuilderByBean(ESCISearchBean bean) {
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        Collection<Long> classIds = bean.getClassIds();
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if (bean.getCdt() != null) {
            query.must(ESUtil.cdtToBuilder(bean.getCdt()));
        }
        bean.setDomainId(domainId);
        // 做个适配，为了数据超市，是不区分租户
        if (bean.getDomainId() != 0) {
            query.must(QueryBuilders.termQuery("domainId", bean.getDomainId()));
        }
        if (!BinaryUtils.isEmpty(bean.getDiagramId())) {
            query.must(QueryBuilders.termQuery("diagramId.keyword", bean.getDiagramId()));
        }
        if (bean.getCiPrimaryKeys() != null && bean.getCiPrimaryKeys().size() > 0) {
            List<List<String>> ciPrimaryKeys = new ArrayList<>();
            if(BinaryUtils.isEmpty(bean.getOwnerCode())){
                throw new BinaryException("根据业务主键查询,ownerCode不能为空");
            }
            bean.getCiPrimaryKeys().forEach(keys -> {
                ciPrimaryKeys.add(JSONArray.parseArray(keys, String.class));
            });
            for (List<String> ciPrimaryKey : ciPrimaryKeys) {
                ciPrimaryKey.add(bean.getOwnerCode());
            }
            List<ESCIInfo> ciInfos = ciSvc.getCIInfoListByCIPrimaryKeys(ciPrimaryKeys);
            if (ciInfos == null || ciInfos.size() <= 0) {
                query.must(QueryBuilders.termQuery("id", -1));
                return query;
            }
            query.must(QueryBuilders.termsQuery("id",
                    ciInfos.stream().collect(Collectors.groupingBy(ESCIInfo::getId)).keySet()));
        }
        if (!classIds.isEmpty()) {
            query.must(QueryBuilders.termsQuery("classId", classIds));
        }
        String ownerCode = bean.getOwnerCode();
        if (!BinaryUtils.isEmpty(ownerCode)) {
            query.must(QueryBuilders.termQuery("ownerCode.keyword", ownerCode));
        }
        Collection<Integer> states = bean.getStateCodes();
        if (!states.isEmpty()) {
            query.must(QueryBuilders.termsQuery("state", states));
        }
        Collection<String> ciCodes = bean.getCiCodes();
        if (!ciCodes.isEmpty()) {
            query.must(QueryBuilders.termsQuery("ciCode.keyword", ciCodes));
        }
        Collection<Long> hashCodes = bean.getHashCodes();
        if (!hashCodes.isEmpty()) {
            query.must(QueryBuilders.termsQuery("hashCode", hashCodes));
        }
        Collection<Long> ciIds = bean.getIds();
        if (!ciIds.isEmpty()) {
            query.must(QueryBuilders.termsQuery("id", ciIds));
        }
        Collection<Long> tagIdList = bean.getTagIds();
        BoolQueryBuilder queryTags = QueryBuilders.boolQuery();
        for (Long tagId : tagIdList) {
            ESCITagInfo tag = tagSvc.getById(tagId);
            if (tag != null) {
                QueryBuilder tagQ = tagSvc.getQueryByTag(tag);
                if (tagQ != null) {
                    queryTags.should(tagQ);
                }
            }
        }
        if (tagIdList.size() > 0) {
            query.must(queryTags);
        }
        Map<String, Map<String, Integer>> defTypeMap = new HashMap<>();
        if (bean.getCdt() != null) {
            if (bean.getCdt().getClassId() != null) {
                classIds.add(bean.getCdt().getClassId());
            }
            if (bean.getCdt().getClassIds() != null) {
                classIds.addAll(Arrays.asList(bean.getCdt().getClassIds()));
            }
        }
        Assert.isTrue(!BinaryUtils.isEmpty(classIds) || (BinaryUtils.isEmpty(bean.getAndAttrs())
                && BinaryUtils.isEmpty(bean.getOrAttrs()) && BinaryUtils.isEmpty(bean.getAndClsAttrQueryGroups())
                && BinaryUtils.isEmpty(bean.getOrClsAttrQueryGroups())), "根据属性查询时，分类id不可为空");
        List<ESCIClassInfo> clsList = classSvc.getTargetAttrDefsByClassIds(bean.getDomainId(), classIds);
        clsList.forEach(cls -> cls.getCcAttrDefs().forEach(def -> {
            Map<String, Integer> map = defTypeMap.computeIfAbsent(def.getProName(), k -> new HashMap<>());
            map.put(def.getProStdName(), def.getProType());
        }));

        // 封装关键字查询条件
        List<String> words = bean.getWords();
        // 关键字支持分类名搜索，暂时不用
        // BoolQueryBuilder clsQuery = QueryBuilders.boolQuery();
        BoolQueryBuilder queryWordsWithSearchKeysQuery = QueryBuilders.boolQuery();
        BoolQueryBuilder queryWordsWithoutSearchKeysQuery = QueryBuilders.boolQuery();
        //无searchKeys参数关键字查询
        Map<Long, List<String>> queryAttrsWithoutSearchKeysMap = new ConcurrentHashMap<>();
        //有关键字且没有searchKeys时，需要取分类字段（有searchKeys就没必要再去拿分类字段了--用不到）
        if (!CollectionUtils.isEmpty(words) && BinaryUtils.isEmpty(bean.getSearchKeys())) {
            //传分类id时，根据wordLabel区分要不要查给定分类的label字段
            if (!CollectionUtils.isEmpty(classIds)) {
                Map<Long, List<String>> sourceLabelDefMap = getSourceDefMapByWordLabelAndClassIds(bean.getWordLabel(), classIds);
                if (!CollectionUtils.isEmpty(sourceLabelDefMap)) {
                    queryAttrsWithoutSearchKeysMap.putAll(sourceLabelDefMap);
                }
            } else {
                //没有分类id时，查所有分类的label字段
                List<ESCIClassInfo> classInfos = classSvc.getListByQueryScroll(QueryBuilders.termQuery("domainId", 1L));
                Collection<Long> allClassIds = classInfos.stream().map(ESCIClassInfo::getId).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(allClassIds)) {
                    Map<Long, List<String>> sourceLabelDefMap = this.getSourceDefMapByWordLabelAndClassIds(true, allClassIds);
                    if (!CollectionUtils.isEmpty(sourceLabelDefMap)) {
                        queryAttrsWithoutSearchKeysMap.putAll(sourceLabelDefMap);
                    }
                }
            }
        }
        //searchKey的关键字查询
        boolean wordsBySearchKeyQuery = false;
        //无searchKey的关键字查询
        boolean wordsWithoutSearchKeysQuery = false;
        for (int i = 0; i < words.size(); i++) {
            String word = words.get(i);
            // clsQuery.must(QueryBuilders.multiMatchQuery(word,
            // "className").operator(Operator.AND)
            // .type(Type.PHRASE_PREFIX).lenient(true));
            List<String> keys = new ArrayList<>();
            if (!BinaryUtils.isEmpty(bean.getSearchKeys())) {
                for (String key : bean.getSearchKeys()) {
                    Map<String, Integer> targetMap = defTypeMap.get(key);
                    if (!BinaryUtils.isEmpty(targetMap)) {
                        keys.addAll(targetMap.keySet().stream().map(targetKey -> "attrs." + targetKey)
                                .collect(Collectors.toList()));
                    } else {
                        keys.add(key);
                    }
                }
            }
            if (!CollectionUtils.isEmpty(keys)) {
                keys = keys.stream().distinct().collect(Collectors.toList());
                queryWordsWithSearchKeysQuery.must(QueryBuilders.multiMatchQuery(word, keys.toArray(new String[]{}))
                        .operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
                wordsBySearchKeyQuery = true;
            }
            if (!CollectionUtils.isEmpty(queryAttrsWithoutSearchKeysMap)) {
                for (Map.Entry<Long, List<String>> entry : queryAttrsWithoutSearchKeysMap.entrySet()) {
                    BoolQueryBuilder classDefQuery = QueryBuilders.boolQuery();
                    Long classId = entry.getKey();
                    List<String> labelDefs = entry.getValue();
                    if (CollectionUtils.isEmpty(labelDefs)) {
                        continue;
                    }
                    List<String> labelSearchDefs = new ArrayList<>();
                    labelDefs.forEach(labelDef -> labelSearchDefs.add("attrs." + labelDef));
                    classDefQuery.must(QueryBuilders.termQuery("classId", classId));
                    classDefQuery.must(QueryBuilders.multiMatchQuery(word, labelSearchDefs.toArray(new String[]{}))
                            .operator(Operator.AND)
                            .type(Type.PHRASE_PREFIX).lenient(true));
                    queryWordsWithoutSearchKeysQuery.should(classDefQuery);
                }
                String[] baseSearchDefs = new String[]{"ciCode", "ciPrimaryKey"};
                BoolQueryBuilder classBaseDefQuery = QueryBuilders.boolQuery();
                classBaseDefQuery.must(QueryBuilders.termsQuery("classId", queryAttrsWithoutSearchKeysMap.keySet()));
                classBaseDefQuery.must(QueryBuilders.multiMatchQuery(word, baseSearchDefs)
                        .operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
                queryWordsWithoutSearchKeysQuery.should(classBaseDefQuery);
                wordsWithoutSearchKeysQuery = true;
            }
        }
        if (wordsBySearchKeyQuery) {
            // List<ESCIClassInfo> classInfos = classSvc.getListByQuery(clsQuery);
            // if (!BinaryUtils.isEmpty(classInfos)) {
            // Set<Long> clsIds =
            // classInfos.stream().map(ESCIClassInfo::getId).collect(Collectors.toSet());
            // query.should(QueryBuilders.termsQuery("classId", clsIds));
            // query.should(queryWords);
            // } else {
            query.must(queryWordsWithSearchKeysQuery);
            // }
        }
        if (wordsWithoutSearchKeysQuery) {
            query.must(queryWordsWithoutSearchKeysQuery);
        }

        // 属性查询 OR 关系
        Collection<ESAttrBean> orAttrs = bean.getOrAttrs();
        BoolQueryBuilder orQuery = QueryBuilders.boolQuery();
        for (ESAttrBean attr : orAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            Integer proType = 3;
            Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
            String str = String.valueOf(map.get("key"));
            proType = Integer.valueOf(map.get("type").toString());
            if (BinaryUtils.isEmpty(value)) {
                continue;
            }
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == -1) {
                orQuery.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(str, value)));
            } else if (opType == 1) {
                orQuery.should(QueryBuilders.termQuery(str, value));
            } else if (opType == 2) {
                orQuery.should(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
            } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                if (proType.intValue() == 7) {
                    Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                    Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                    value = date.getTime();
                }
                switch (opType) {
                    case 3:
                        orQuery.should(QueryBuilders.rangeQuery(str).lt(value));
                        break;
                    case 4:
                        orQuery.should(QueryBuilders.rangeQuery(str).lte(value));
                        break;
                    case 5:
                        orQuery.should(QueryBuilders.rangeQuery(str).gt(value));
                        break;
                    case 6:
                        orQuery.should(QueryBuilders.rangeQuery(str).gte(value));
                        break;
                }
            }
        }
        if (orAttrs.size() > 0) {
            query.must(orQuery);
        }
        // 属性查询 And 关系
        Collection<ESAttrBean> andAttrs = bean.getAndAttrs();
        BoolQueryBuilder andQuery = QueryBuilders.boolQuery();
        for (ESAttrBean attr : andAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            Integer proType = 3;
            Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
            String str = String.valueOf(map.get("key"));
            proType = Integer.valueOf(map.get("type").toString());
            if (BinaryUtils.isEmpty(value)) {
                continue;
            }
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == -1) {
                andQuery.must(QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(str, value)));
            } else if (opType == 1) {
                andQuery.must(QueryBuilders.termQuery(str, value));
            } else if (opType == 2) {
                andQuery.must(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
            } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                if (proType.intValue() == 7) {
                    Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                    Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                    value = date.getTime();
                }
                switch (opType) {
                    case 3:
                        andQuery.must(QueryBuilders.rangeQuery(str).lt(value));
                        break;
                    case 4:
                        andQuery.must(QueryBuilders.rangeQuery(str).lte(value));
                        break;
                    case 5:
                        andQuery.must(QueryBuilders.rangeQuery(str).gt(value));
                        break;
                    case 6:
                        andQuery.must(QueryBuilders.rangeQuery(str).gte(value));
                        break;
                }
            }
        }
        if (andAttrs.size() > 0) {
            query.must(andQuery);
        }
        List<ESAttrBean> dcvAndAttrs = bean.getAndDcvAttrs();
        BoolQueryBuilder queryDcvAndAttrs = QueryBuilders.boolQuery();
        for (ESAttrBean attr : dcvAndAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == 1) {
                queryDcvAndAttrs.must(QueryBuilders.termQuery("dcvExtAttrs." + key.trim() + ".keyword", value));
            } else if (opType == 2) {
                queryDcvAndAttrs.must(QueryBuilders.multiMatchQuery(value, "dcvExtAttrs." + key.trim())
                        .operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
            }
        }
        if (dcvAndAttrs.size() > 0) {
            query.must(queryDcvAndAttrs);
        }
        List<ESAttrBean> dcvOrAttrs = bean.getOrDcvAttrs();
        BoolQueryBuilder queryDcvOrAttrs = QueryBuilders.boolQuery();
        for (ESAttrBean attr : dcvOrAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == 1) {
                queryDcvOrAttrs.should(QueryBuilders.termQuery("dcvExtAttrs." + key.trim() + ".keyword", value));
            } else if (opType == 2) {
                queryDcvOrAttrs.should(QueryBuilders.multiMatchQuery(value, "dcvExtAttrs." + key.trim())
                        .operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
            }
        }
        if (dcvOrAttrs.size() > 0) {
            query.must(queryDcvOrAttrs);
        }
        List<ESOrRuleItem> ruleItems = bean.getRuleItems();
        BoolQueryBuilder ruleItemsBuilder = QueryBuilders.boolQuery();
        for (ESOrRuleItem ruleItem : ruleItems) {
            BoolQueryBuilder itemQuery = QueryBuilders.boolQuery();
            List<ESAndAttrRule> attrRules = ruleItem.getAttrRules();
            for (ESAndAttrRule attrRule : attrRules) {
                itemQuery.must(getQueryOp(attrRule));
            }
            ruleItemsBuilder.should(itemQuery);
        }
        if (ruleItems.size() > 0) {
            query.must(ruleItemsBuilder);
        }
        if (bean.getOrClsAttrQueryGroups() != null && bean.getOrClsAttrQueryGroups().size() > 0) {
            BoolQueryBuilder orGroupQuery = QueryBuilders.boolQuery();
            List<ClsAttrQueryGroup> orGroup = bean.getOrClsAttrQueryGroups();
            for (ClsAttrQueryGroup orItem : orGroup) {
                BoolQueryBuilder orGroupQueryItem = QueryBuilders.boolQuery();
                Long clsId = orItem.getClassId();
                orGroupQueryItem.must(QueryBuilders.termQuery("classId", clsId));
                List<ESAttrBean> andAttrQuery = orItem.getAndAttrs();
                if (andAttrQuery != null && andAttrQuery.size() > 0) {
                    for (ESAttrBean andAttr : andAttrQuery) {
                        String key = andAttr.getKey();
                        int opType = andAttr.getOptType();
                        Object value = andAttr.getValue();
                        // 拼装下查询条件queryAttr
                        BoolQueryBuilder andQueryAttr = QueryBuilders.boolQuery();
                        Integer proType = 3;
                        Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
                        String str = String.valueOf(map.get("key"));
                        proType = Integer.valueOf(map.get("type").toString());
                        // opType=1表示绝对等于,opType=2表示全文检索
                        if (opType == 1) {
                            andQueryAttr.must(QueryBuilders.termQuery(str, value));
                        } else if (opType == 2) {
                            andQueryAttr.must(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
                        } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                            Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                            if (proType.intValue() == 7) {
                                Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                                Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                                value = date.getTime();
                            }
                            switch (opType) {
                                case 3:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).lt(value));
                                    break;
                                case 4:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).lte(value));
                                    break;
                                case 5:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).gt(value));
                                    break;
                                case 6:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).gte(value));
                                    break;
                            }
                        }
                        orGroupQueryItem.must(andQueryAttr);
                    }
                }
                List<ESAttrBean> orAttrQuery = orItem.getOrAttrs();
                if (orAttrQuery != null && orAttrQuery.size() > 0) {
                    for (ESAttrBean orAttr : orAttrQuery) {
                        String key = orAttr.getKey();
                        int opType = orAttr.getOptType();
                        Object value = orAttr.getValue();
                        // 拼装下查询条件queryAttr
                        BoolQueryBuilder orQueryAttr = QueryBuilders.boolQuery();
                        Integer proType = 3;
                        Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
                        String str = String.valueOf(map.get("key"));
                        proType = Integer.valueOf(map.get("type").toString());
                        // opType=1表示绝对等于,opType=2表示全文检索
                        if (opType == 1) {
                            orQueryAttr.should(QueryBuilders.termQuery(str, value));
                        } else if (opType == 2) {
                            orQueryAttr.should(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
                        } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                            Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                            if (proType.intValue() == 7) {
                                Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                                Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                                value = date.getTime();
                            }
                            switch (opType) {
                                case 3:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).lt(value));
                                    break;
                                case 4:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).lte(value));
                                    break;
                                case 5:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).gt(value));
                                    break;
                                case 6:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).gte(value));
                                    break;
                            }
                        }
                        orGroupQueryItem.should(orQueryAttr);
                    }
                }
                orGroupQuery.should(orGroupQueryItem);
            }
            query.must(orGroupQuery);
        }


        if (bean.getAndClsAttrQueryGroups() != null && bean.getAndClsAttrQueryGroups().size() > 0) {
            BoolQueryBuilder andGroupQuery = QueryBuilders.boolQuery();
            List<ClsAttrQueryGroup> andGroup = bean.getAndClsAttrQueryGroups();
            for (ClsAttrQueryGroup andItem : andGroup) {
                BoolQueryBuilder andGroupQueryItem = QueryBuilders.boolQuery();
                Long clsId = andItem.getClassId();
                andGroupQuery.must(QueryBuilders.termQuery("classId", clsId));
                List<ESAttrBean> andAttrQuery = andItem.getAndAttrs();
                if (andAttrQuery != null && andAttrQuery.size() > 0) {
                    for (ESAttrBean andAttr : andAttrQuery) {
                        String key = andAttr.getKey();
                        int opType = andAttr.getOptType();
                        Object value = andAttr.getValue();
                        // 拼装下查询条件queryAttr
                        BoolQueryBuilder andQueryAttr = QueryBuilders.boolQuery();
                        Integer proType = 3;
                        Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
                        String str = String.valueOf(map.get("key"));
                        proType = Integer.valueOf(map.get("type").toString());
                        // opType=1表示绝对等于,opType=2表示全文检索
                        if (opType == 1) {
                            andQueryAttr.must(QueryBuilders.termQuery(str, value));
                        } else if (opType == 2) {
                            andQueryAttr.must(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
                        } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                            Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                            if (proType.intValue() == 7) {
                                Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                                Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                                value = date.getTime();
                            }
                            switch (opType) {
                                case 3:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).lt(value));
                                    break;
                                case 4:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).lte(value));
                                    break;
                                case 5:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).gt(value));
                                    break;
                                case 6:
                                    andQueryAttr.must(QueryBuilders.rangeQuery(str).gte(value));
                                    break;
                            }
                        }
                        andGroupQueryItem.must(andQueryAttr);
                    }
                }
                List<ESAttrBean> orAttrQuery = andItem.getOrAttrs();
                if (orAttrQuery != null && orAttrQuery.size() > 0) {
                    for (ESAttrBean orAttr : orAttrQuery) {
                        String key = orAttr.getKey();
                        int opType = orAttr.getOptType();
                        Object value = orAttr.getValue();
                        // 拼装下查询条件queryAttr
                        BoolQueryBuilder orQueryAttr = QueryBuilders.boolQuery();
                        Integer proType = 3;
                        Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, key, opType);
                        String str = String.valueOf(map.get("key"));
                        proType = Integer.valueOf(map.get("type").toString());
                        // opType=1表示绝对等于,opType=2表示全文检索
                        if (opType == 1) {
                            orQueryAttr.should(QueryBuilders.termQuery(str, value));
                        } else if (opType == 2) {
                            orQueryAttr.should(QueryBuilders.multiMatchQuery(value, str).operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
                        } else if (opType == 3 || opType == 4 || opType == 5 || opType == 6) {
                            Assert.isTrue(proType.intValue() <= 2 || proType.intValue() == 7, "仅数值与日期类型属性支持区间查询");
                            if (proType.intValue() == 7) {
                                Date date = CheckAttrUtil.getDateByDefaultRule(value.toString());
                                Assert.isTrue(!BinaryUtils.isEmpty(date), "不支持的时间格式[" + value + "]");
                                value = date.getTime();
                            }
                            switch (opType) {
                                case 3:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).lt(value));
                                    break;
                                case 4:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).lte(value));
                                    break;
                                case 5:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).gt(value));
                                    break;
                                case 6:
                                    orQueryAttr.should(QueryBuilders.rangeQuery(str).gte(value));
                                    break;
                            }
                        }
                        andGroupQueryItem.should(orQueryAttr);
                    }
                }
                andGroupQuery.must(andGroupQueryItem);
            }
            query.must(andGroupQuery);
        }

        Collection<String> highLightFields = bean.getHighLightFields();
        if (bean.isHighLight() && !BinaryUtils.isEmpty(highLightFields)) {
            List<String> targetFields = new ArrayList<>();
            for (String highLightField : highLightFields) {
                Map<String, String> map = this.getTargetAttrNameStr(defTypeMap, highLightField, 1);
                targetFields.add(map.get("key"));
            }
            bean.setHighLightFields(targetFields);
        }
        return query;
    }

    private Map<Long, List<String>> getSourceDefMapByWordLabelAndClassIds(Boolean wordLabel, Collection<Long> classIds) {
        Map<Long, List<String>> defMap = new ConcurrentHashMap<>();
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termsQuery("id", classIds));
        List<ESCIClassInfo> ciClassInfos = classSvc.getSourceCIClassDefByQuery(query);
        if (!CollectionUtils.isEmpty(ciClassInfos)) {
            for (ESCIClassInfo cls : ciClassInfos) {
                List<String> defs = new ArrayList<>();
                cls.getCcAttrDefs().forEach(def -> {
                    if (wordLabel) {
                        if (def.getIsCiDisp() != null && def.getIsCiDisp() == 1) {
                            defs.add(def.getProStdName());
                        }
                    } else {
                        defs.add(def.getProStdName());
                    }
                });
                defMap.put(cls.getId(), defs);
            }
        }
        return defMap;
    }

    /**
     * <b> 获取关系查询条件
     *
     * @param bean
     * @return
     */
    public QueryBuilder getRltQueryBuilderByBean(ESRltSearchBean bean) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if (bean.getDomainId() != 0) {
            query.must(QueryBuilders.termQuery("domainId", bean.getDomainId()));
        }
        if (!BinaryUtils.isEmpty(bean.getOwnerCode())) {
            query.must(QueryBuilders.termQuery("ownerCode.keyword", bean.getOwnerCode()));
        }
        List<String> keywords = bean.getKeywords();
        List<Long> rltClass = bean.getRltClassIds();
        if (bean.getClassCode() != null && !"".equals(bean.getClassCode())) {
            List<ESCIClassInfo> clss = esRltClassSvc
                    .getListByQuery(QueryBuilders.termQuery("classCode.keyword", bean.getClassCode()));
            if (clss != null && clss.size() > 0) {
                query.must(QueryBuilders.termsQuery("classId",
                        clss.stream().collect(Collectors.groupingBy(ESCIClassInfo::getId)).keySet()));
            } else {
                query.must(QueryBuilders.termQuery("id", -1));
                return query;
            }
        }
        if (bean.getSourceCiPrimaryKeys() != null && bean.getSourceCiPrimaryKeys().size() > 0) {
            List<List<String>> ciPrimaryKeys = new ArrayList<>();
            bean.getSourceCiPrimaryKeys().forEach(keys -> {
                ciPrimaryKeys.add(JSONArray.parseArray(keys, String.class));
            });
            List<ESCIInfo> sourceCis = ciSvc.getCIInfoListByCIPrimaryKeys(ciPrimaryKeys);
            if (sourceCis == null || sourceCis.size() <= 0) {
                query.must(QueryBuilders.termQuery("id", -1));
                return query;
            }
            query.must(QueryBuilders.termsQuery("sourceCiId",
                    sourceCis.stream().collect(Collectors.groupingBy(ESCIInfo::getId)).keySet()));
        }
        if (bean.getTargetCiPrimaryKeys() != null && bean.getTargetCiPrimaryKeys().size() > 0) {
            List<List<String>> ciPrimaryKeys = new ArrayList<>();
            bean.getTargetCiPrimaryKeys().forEach(keys -> {
                ciPrimaryKeys.add(JSONArray.parseArray(keys, String.class));
            });
            List<ESCIInfo> targetCis = ciSvc.getCIInfoListByCIPrimaryKeys(ciPrimaryKeys);
            if (targetCis == null || targetCis.size() <= 0) {
                query.must(QueryBuilders.termQuery("id", -1));
                return query;
            }
            query.must(QueryBuilders.termsQuery("targetCiId",
                    targetCis.stream().collect(Collectors.groupingBy(ESCIInfo::getId)).keySet()));
        }
        if (rltClass.size() > 0) {
            query.must(QueryBuilders.termsQuery("classId", rltClass));
        }
        if (bean.getRltCodes() != null && bean.getRltCodes().size() > 0) {
            query.must(QueryBuilders.termsQuery("ciCode.keyword", bean.getRltCodes()));
        }
        if (bean.getRltUniqueCodes() != null && bean.getRltUniqueCodes().size() > 0) {
            query.must(QueryBuilders.termsQuery("uniqueCode.keyword", bean.getRltUniqueCodes()));
        }
        if (!bean.getSourceCiIds().isEmpty()) {
            query.must(QueryBuilders.termsQuery("sourceCiId", bean.getSourceCiIds()));
        }
        if (!bean.getTargetCiIds().isEmpty()) {
            query.must(QueryBuilders.termsQuery("targetCiId", bean.getTargetCiIds()));
        }
        if (bean.getSourceCiCodes() != null && bean.getSourceCiCodes().size() > 0) {
            query.must(QueryBuilders.termsQuery("sourceCiCode.keyword", bean.getSourceCiCodes()));
        }
        if (bean.getTargetCiCodes() != null && bean.getTargetCiCodes().size() > 0) {
            query.must(QueryBuilders.termsQuery("targetCiCode.keyword", bean.getTargetCiCodes()));
        }
        if (!bean.getSourceClassIds().isEmpty()) {
            query.must(QueryBuilders.termsQuery("sourceClassId", bean.getSourceClassIds()));
        }
        if (!bean.getTargetClassIds().isEmpty()) {
            query.must(QueryBuilders.termsQuery("targetClassId", bean.getTargetClassIds()));
        }
        if (!bean.getCiRltLvl().isEmpty()) {
            query.must(QueryBuilders.termsQuery("ciRltLvl", bean.getCiRltLvl()));
        }
        BoolQueryBuilder subQuery = QueryBuilders.boolQuery();
        for (String keyword : keywords) {
            if (keyword.length() > 0) {
                subQuery.should(
                        QueryBuilders
                                .multiMatchQuery(keyword, "attrs.*", "sourceCiCode", "targetCiCode",
                                        "targetCiPrimaryKey", "sourceCiPrimaryKey", "sourceCiSearchValues",
                                        "targetCiSearchValues")
                                .operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
            }
        }
        if (keywords.size() > 0) {
            query.must(subQuery);
        }
        List<ESAttrBean> andAttrs = bean.getAndAttrs();
        BoolQueryBuilder queryAttrs = QueryBuilders.boolQuery();
        for (ESAttrBean attr : andAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == 1) {
                queryAttrs.must(QueryBuilders.termQuery("attrs." + key.toUpperCase() + ".keyword", value));
            } else if (opType == 2) {
                queryAttrs.must(QueryBuilders.multiMatchQuery(value, "attrs." + key.toUpperCase())
                        .operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
            }
        }
        if (andAttrs.size() > 0) {
            query.must(queryAttrs);
        }
        List<ESAttrBean> orAttrs = bean.getOrAttrs();
        BoolQueryBuilder queryOrAttrs = QueryBuilders.boolQuery();
        for (ESAttrBean attr : orAttrs) {
            String key = attr.getKey();
            Object value = attr.getValue();
            int opType = attr.getOptType();
            // opType=1表示绝对等于,opType=2表示全文检索
            if (opType == 1) {
                queryOrAttrs.must(QueryBuilders.termQuery("attrs." + key.toUpperCase() + ".keyword", value));
            } else if (opType == 2) {
                queryOrAttrs.must(QueryBuilders.multiMatchQuery(value, "attrs." + key.toUpperCase())
                        .operator(Operator.AND).type(Type.PHRASE_PREFIX).lenient(true));
            }
        }
        if (orAttrs.size() > 0) {
            query.must(queryOrAttrs);
        }
        if (bean.getCdt() != null) {
            BoolQueryBuilder andQuery = ESUtil.cdtToBuilder(bean.getCdt());
            query.must(andQuery);
        }
        if(bean.getCheckData()) {
            BoolQueryBuilder shouleQueryBuilder = QueryBuilders.boolQuery();
            if (!CollectionUtils.isEmpty(bean.getNotMathSourceClassIds())) {
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                BoolQueryBuilder sourceClassId = boolQueryBuilder.mustNot(QueryBuilders.termsQuery("sourceClassId", bean.getNotMathSourceClassIds()));
                shouleQueryBuilder.should(sourceClassId);
            }
            if (!CollectionUtils.isEmpty(bean.getNotMatchTargetClassIds())) {
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                BoolQueryBuilder targetClassId = boolQueryBuilder.mustNot(QueryBuilders.termsQuery("targetClassId", bean.getNotMatchTargetClassIds()));
                shouleQueryBuilder.should(targetClassId);
            }
            query.must(shouleQueryBuilder);
        }
        return query;
    }

    /**
     * 根据属性模板校验属性-校验不通过则抛出异常
     *
     * @param attrDefs 属性模板
     * @param attrs    属性
     */
    public void validAttrs(List<CcCiAttrDef> attrDefs, Map<String, String> attrs) {
        validAttrs(attrDefs, attrs, false);
        // if (attrDefs == null || attrDefs.size() <= 0)
        // return;
        // // 组装校验链所需参数
        // Map<String, Object> validParamMap = new HashMap<>();
        // validParamMap.put("attrDefs", attrDefs);
        // validParamMap.put("attrs", attrs);
        // // 必填项校验
        // BaseJudgeProcess attrRequiredJudge = new AttrRequiredJudge();
        // // 类型匹配校验-未实现
        // // 对其中枚举类型校验
        // // 约束规则校验-未实现
        // // 将所有链串联，开始校验
        // attrRequiredJudge.valid(validParamMap);
    }


    public CIState generateStateAndValidAttrs(List<CcCiAttrDef> attrDefs, Map<String, String> attrs, List<String> ciPKAttrDefNames) {
        Assert.isTrue(!BinaryUtils.isEmpty(ciPKAttrDefNames), "业务主键定义为空");
        CIState state = CIState.CREATE_INIT;
        Optional<String> hasPKEmpty = ciPKAttrDefNames.stream().filter(pkAttrName -> BinaryUtils.isEmpty(attrs.get(pkAttrName))).findFirst();
        if (!hasPKEmpty.isPresent()) {
            state = CIState.CREATE_PENDING;
        }
        boolean hasEmpty = false;
        Map<String, Integer> errorMsgs = CheckAttrUtil.validateAttrValType(attrDefs, attrs);
        if(!BinaryUtils.isEmpty(errorMsgs)){
            for (Map.Entry<String, Integer> errEntry : new HashMap<>(errorMsgs).entrySet()) {
                if (errEntry.getValue() == CheckAttrUtil.EMPTY_VAL || errEntry.getValue() == CheckAttrUtil.MUST_VAL) {
                    hasEmpty = true;
                    errorMsgs.remove(errEntry.getKey());
                }
            }
        }
        Assert.isTrue(BinaryUtils.isEmpty(errorMsgs), String.join("|", errorMsgs.keySet()));
        if(!hasEmpty){
            state = CIState.CREATE_COMPLETE;
        }
        return state;
    }

    /**
     * 根据关系分类属性模板以及rltcode以及属性信息拼装唯一标识
     *
     * @param attrDefs
     * @param attrs
     * @param rltCode
     * @return
     */
    public String getUniqueCode(List<CcCiAttrDef> attrDefs, Map<String, String> attrs, String rltCode) {
        StringBuffer appendCode = new StringBuffer(1000);
        if (attrDefs != null && attrDefs.size() > 0) {
            List<CcCiAttrDef> isMajorDefs = attrDefs.stream()
                    .filter(def -> def.getIsMajor() != null && def.getIsMajor().intValue() == 1)
                    .collect(Collectors.toList());
            Collections.sort(isMajorDefs, (o1, o2)->{
                int o1HashCode = o1.getProStdName().hashCode();
                int o2HashCode = o2.getProStdName().hashCode();
                if (o1HashCode == o2HashCode) {
                    return 0;
                } else if (o1HashCode < o2HashCode) {
                    return -1;
                } else {
                    return 1;
                }
            });
            if (isMajorDefs != null && isMajorDefs.size() > 0) {
                attrs = attrs == null ? new HashMap<>() : attrs;
                for (CcCiAttrDef def : isMajorDefs) {
                    String defKey = def.getProStdName();
                    String attrVal = attrs.get(defKey);
                    attrVal = (attrVal == null || "".equals(attrVal.trim())) ? "NULL" : attrVal;
                    appendCode.append("-").append(attrVal);
                }
            }
        }
        // appendCode = appendCode != null && !"".equals(appendCode) ?
        // appendCode.toUpperCase() : appendCode;
        return "UK_" + (appendCode.length() == 0 ? rltCode : rltCode + appendCode.toString());
    }

    /**
     * 根据属性模板校验属性
     *
     * @param attrDefs       属性模板
     * @param attrs          属性
     * @param notThrownException 是否返回异常信息 true:返回异常信息 false:不返回异常信息（不通过抛出异常）
     * @return 异常信息（数组为空或size<=0则代表所有属性校验通过）
     */
    public Map<String, Integer> validAttrs(List<CcCiAttrDef> attrDefs, Map<String, String> attrs,
                                           boolean notThrownException) {
        Map<String, Integer> errorMsgs = CheckAttrUtil.validateAttrValType(attrDefs, attrs);
        if (notThrownException) {
            //私有库保存ci去除非空校验
            new HashMap<>(errorMsgs).forEach((k, v) -> {
                if (v == CheckAttrUtil.EMPTY_VAL || v == CheckAttrUtil.MUST_VAL) {
                    errorMsgs.remove(k);
                }
            });
            return errorMsgs;
        } else {
            String errorMsg = "";
            errorMsg = (errorMsgs != null && errorMsgs.size() > 0) ? new ArrayList<>(errorMsgs.keySet()).get(0) : "";
            Assert.isTrue(errorMsgs == null || errorMsgs.size() <= 0, errorMsg);
            return null;
        }
    }

    /**
     * 获取查询对象
     *
     * @param attrRule
     * @return
     */
    public QueryBuilder getQueryOp(ESAndAttrRule attrRule) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        String key = attrRule.getKey();
        String value = attrRule.getValue();
        int optType = attrRule.getOptType();
        if (!"ciCode".equals(key)) {
            key = "attrs." + key + ".keyword";
        }
        switch (optType) {
            case 1:
                // 等于
                query.must(QueryBuilders.termQuery(key, value));
                break;
            case 2:
                // 不等于
                query.mustNot(QueryBuilders.termQuery(key, value));
                break;
            case 3:
                // 小于
                query.must(QueryBuilders.rangeQuery(key).lt(value));
                break;
            case 4:
                // 小于或等于
                query.must(QueryBuilders.rangeQuery(key).lte(value));
                break;
            case 5:
                // 大于
                query.must(QueryBuilders.rangeQuery(key).gt(value));
                break;
            case 6:
                // 大于或等于
                query.must(QueryBuilders.rangeQuery(key).gte(value));
                break;
            case 7:
                // 模糊匹配
                if (!"ciCode".equals(key)) {
                    key = "attrs." + key.toUpperCase();
                }
                query.must(QueryBuilders.multiMatchQuery(value.toString(), key).operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
                break;
            case 8:
                // 模糊匹配取反
                if (!"ciCode".equals(key)) {
                    key = "attrs." + key.toUpperCase();
                }
                query.mustNot(QueryBuilders.multiMatchQuery(value.toString(), key).operator(Operator.AND)
                        .type(Type.PHRASE_PREFIX).lenient(true));
                break;
            case 9:
                // 包含
                String[] words = value.toString().split("\\,");
                query.must(QueryBuilders.termsQuery(key, words));
                break;
            case 10:
                // 不包含
                String[] words1 = value.toString().split("\\,");
                query.mustNot(QueryBuilders.termsQuery(key, words1));
                break;
            default:
                break;
        }
        return query;
    }

    private Map<String, String> getTargetAttrNameStr(Map<String, Map<String, Integer>> defTypeMap, String key, Integer optionType) {
        Map<String, String> map = new HashMap<>();
        String str = "attrs." + key.toUpperCase();
        Integer proType = 3;
        Map<String, Integer> targetMap = defTypeMap.get(key);
        // 当查询的属性名称相同，但类型不同时(数值与字符串)，只取第一个匹配的映射
        if (!BinaryUtils.isEmpty(targetMap)) {
            String targetKey = new ArrayList<>(targetMap.keySet()).get(0);
            str = "attrs." + targetKey;
            proType = new ArrayList<>(targetMap.values()).get(0);
        }
        // opType=1表示绝对等于,opType=2表示全文检索
        if (optionType == -1 || optionType == 1) {
            // 由于日期类型存储时间戳，预留属性后缀_date存储日期字符串
            if (proType.intValue() == 7) {
                str = str + "_date";
            }
            if (proType.intValue() > 2) {
                str += ".keyword";
            }
        } else if (optionType == 2) {
            // 由于日期类型存储时间戳，预留属性后缀_date存储日期字符串
            if (proType.intValue() == 7) {
                str = str + "_date";
            }
        }
        map.put("key", str);
        map.put("type", proType.toString());
        return map;
    }
}
